﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Perceiveit.Xml
{

    public class XSubClassFactory
    {
        public static XSubClassFactory<T, U> Simple<T, U>(string name, string namespaceName) where U: T, new() where T : XEntity
        {
            XSimpleFactory<U> simple = new XSimpleFactory<U>(name, namespaceName);
            return new XSubClassFactory<T, U>(simple);
        }

        public static XSubClassFactory<T, U> Complex<T, U>(XFactory<U> complexfactory) where U : T where T : XEntity
        {
            return new XSubClassFactory<T, U>(complexfactory);
        }

    }

    /// <summary>
    /// A factory that will impersonate a factory of the base class but instaniate items of the sub class. 
    /// Use the XSubClassFactory.Simple/Complex static methods to create instances of this type
    /// </summary>
    /// <typeparam name="T">The base class to impersonate e.g. Person</typeparam>
    /// <typeparam name="U">The actual class to instaniate e.g Employee</typeparam>
    /// <remarks>We use this class because a Factory&lt;Employee&gt; cannot be cast to a Factory&lt;Person&gt; despite their typed inheritance</remarks>
    public class XSubClassFactory<T,U> : XFactory<T> where U : T where T : XEntity
    {
        private XFactory<U> _subfactory;

        internal XSubClassFactory(XFactory<U> actualfactory) : base()
        {
            _subfactory = actualfactory;
        }

        public override bool IsKnownEntity(T entity, out XFactory<T> factory)
        {
            XFactory<U> known;
            if (_subfactory.IsKnownEntity((U)entity, out known))
            {
                factory = this;
                return true;
            }
            else
            {
                factory = null;
                return false;
            }
        }

        public override bool IsKnownName(System.Xml.Linq.XName name, out XFactory<T> factory)
        {
            XFactory<U> known;
            if (_subfactory.IsKnownName(name, out known))
            {
                factory = this;
                return true;
            }
            else
            {
                factory = null;
                return false;
            }
        }

        public override T GetEntityForName(System.Xml.Linq.XName name)
        {
            return this._subfactory.GetEntityForName(name);
        }

        public override System.Xml.Linq.XName GetNameForEntity(T entity)
        {
            U sub = (U)entity;
            return _subfactory.GetNameForEntity(sub);
        }

    }
}
